package com.design_patterns.visitor;

/**
 * 访问者模式
 *
 * @author: 李德才
 * @description:
 * @create: 2020-09-04 21:15
 **/

/**
 * 访问者模式：
 * 将作用于某种数据结构中的各元素的操作分离出来封装成独立的类，
 * 使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作，
 * 为数据结构中的每个元素提供多种访问方式。
 * 它将对数据的操作与数据结构进行分离，
 * 是行为类模式中最复杂的一种模式。
 */
public class Computer {
    ComputerPart cpu = new CPU();
    ComputerPart memory = new Memory();
    ComputerPart board = new Board();


    public static void main(String[] args) {

        /**
         *  PersonVisitor 继承了 Visitor ,
         *  重写了 void visitCpu(CPU cpu); void visitMemory(Memory memory); void visitBoard(Board board);
         *  方法中定义了需要传递的引用类型
         */
        PersonVisitor p = new PersonVisitor();
        /**
         *  new Computer().accept(Visitor v); 的参数是 Visitor ，
         *  但是 PersonVisitor 继承了  Visitor ，根据里氏替换原则，父类出现的地方子类就可以出现，所以可以直接传递 PersonVisitor
         */
        /**
         * new Computer().accept(Visitor v ); 方法中分别调用了以下三个方法
         *      cpu.accept(v);
         *      memory.accept(v);
         *      board.accept(v);
         * 而此时的  Visitor v ,本质是 PersonVisitor，
         * 就有了接下来的事情 ：
         * 因为 CPU 中的 accept(Visitor v) 调用的就是 v.visitCpu
         *      所以cpu.accept(v); 可以直接执行 PersonVisitor 中的 visitCpu(CPU cpu)
         * 因为 Memory 中的 accept(Visitor v) 调用的就是 v.visitMemory
         *      所以 memory.accept(v); 可以直接执行 PersonVisitor 中的 visitMemory(Memory memory)
         */
        new Computer().accept(p);
        System.err.println(p.totalPrice);
    }


    public void accept(Visitor v) {
        this.cpu.accept(v);
        this.memory.accept(v);
        this.board.accept(v);
    }
}

abstract class ComputerPart {
    abstract void accept(Visitor v);

    abstract double getPrice();
}

class CPU extends ComputerPart {

    @Override
    void accept(Visitor v) {
//        注意这里是  visitCpu ，每个类调用的方法都不一样
        v.visitCpu(this);
    }

    @Override
    double getPrice() {
        return 500;
    }
}

class Memory extends ComputerPart {

    @Override
    void accept(Visitor v) {
        v.visitMemory(this);
    }

    @Override
    double getPrice() {
        return 300;
    }
}

class Board extends ComputerPart {

    @Override
    void accept(Visitor v) {
        v.visitBoard(this);
    }

    @Override
    double getPrice() {
        return 200;
    }
}

interface Visitor {
    void visitCpu(CPU cpu);

    void visitMemory(Memory memory);

    void visitBoard(Board board);
}

class PersonVisitor implements Visitor {

    double totalPrice = 0.0;

    @Override
    public void visitCpu(CPU cpu) {
        totalPrice += cpu.getPrice() * 0.9;
    }

    @Override
    public void visitMemory(Memory memory) {
        totalPrice += memory.getPrice() * 0.85;
    }

    @Override
    public void visitBoard(Board board) {
        totalPrice += board.getPrice() * 0.95;
    }
}

class CorpVisitor implements Visitor {
    double totalPrice = 0.0;

    @Override
    public void visitCpu(CPU cpu) {
        totalPrice += cpu.getPrice() * 0.6;
    }

    @Override
    public void visitMemory(Memory memory) {
        totalPrice += memory.getPrice() * 0.75;
    }

    @Override
    public void visitBoard(Board board) {
        totalPrice += board.getPrice() * 0.75;
    }
}